home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / waisgate / irtfiles.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  29KB  |  1,013 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.
  4.  
  5.    Brewster@think.com
  6. */
  7.  
  8. /* Change log:
  9.  * $Log: irtfiles.c,v $
  10.  * Revision 1.32  92/05/06  17:32:14  jonathan
  11.  * Added new global for current_filename and current_filecount (from
  12.  * riddle@rice.edu).
  13.  * 
  14.  * Revision 1.31  92/04/30  12:25:09  jonathan
  15.  * changed a couple of s_free's to free's for ULTRIX CC.
  16.  * 
  17.  * Revision 1.30  92/04/29  08:09:55  shen
  18.  * add global variable "_indexable_section", default is true
  19.  * 
  20.  * Revision 1.29  92/04/28  17:53:24  jonathan
  21.  * Replaced directory routines with scandir.
  22.  * 
  23.  * Revision 1.28  92/03/20  11:02:55  jonathan
  24.  * Added code to handle switches for word_pairs and word_postition info.
  25.  * 
  26.  * Revision 1.27  92/02/13  11:23:21  jonathan
  27.  * Removed printable_time() from index logging, since it's done by waislog.
  28.  * 
  29.  * Revision 1.26  92/02/12  13:31:29  jonathan
  30.  * Added "$Log" so RCS will put the log message in the header
  31.  * 
  32. */
  33.  
  34. /* 
  35.  * Indexes the words in a text file.
  36.  * 
  37.  * Port of irtfiles.lisp.
  38.  *
  39.  * -brewster 6/90
  40.  */
  41.  
  42. /* the main functions are:
  43.  *   index_text_file
  44.  *   index_directory
  45.  *
  46.  * Some of the policy issues coded in this file are
  47.  *   What extra weight should the headline get?
  48.  *
  49.  */
  50.  
  51. #include <ctype.h>
  52. #include <string.h>
  53. #include "panic.h"  
  54. #include "irdirent.h"
  55. #include "irhash.h"
  56. #include "cutil.h"
  57. #include "futil.h"
  58. #include "irfiles.h"
  59. #include "irtfiles.h"
  60.  
  61. #ifndef THINK_C
  62. #include <sys/types.h>
  63. #include <sys/stat.h>
  64. #endif /* ndef THINK_C */
  65.  
  66. #define MAX_LINE_LENGTH 1000 /* characters */
  67. #define extra_weight_for_header 10
  68.  
  69. #ifdef UNIX
  70. #define PRINT_AS_INDEXING true /* also defined in irfiles.c */
  71. #else 
  72. #define PRINT_AS_INDEXING false
  73. #endif
  74.  
  75. char* header_flag_1;
  76. char* header_flag_2;
  77. long len_of_files_since_last_delete = 0;
  78. long len_of_files_since_last_flush = 0;
  79. long total_indexed_file_length = 0;
  80.  
  81. boolean indexingForBeta = false;
  82.  
  83. long _indexable_section = 1;
  84.  
  85. char *current_filename = NULL;
  86. int  current_filecount = 0;
  87.  
  88. boolean index_contents = true;
  89. boolean filter_contents;
  90. char filter_program[MAX_LINE_LENGTH];
  91.  
  92. /*  Handling Word Pairs */
  93.  
  94. /* makes a word_pair out of a two words:
  95. make_joint_word("abcdefghijklmnopqrstuvwxyz", "123456789012345678901");
  96.   "abcdefghij1234567890"
  97. make_joint_word("abcdefghijkl", "123");
  98.   "abcdefghij123"
  99. make_joint_word("abc", "123");
  100.   "abc123" */
  101.  
  102. char *make_joint_word(word1, word2)
  103.      char* word1;
  104.      char* word2;
  105. {
  106.   static char new_word[MAX_WORD_LENGTH + 1];
  107.   strncpy(new_word, word1, MAX_WORD_LENGTH / 2);
  108.   strncpy(new_word + MIN(MAX_WORD_LENGTH / 2, strlen(word1)),
  109.       word2, MAX_WORD_LENGTH - (MAX_WORD_LENGTH / 2));
  110.   return(new_word);    
  111. }
  112.  
  113. /* returns 0 is successful, non-0 if error */
  114. static long add_word_before_pairs _AP((char *word, long char_pos,
  115.                        long line_pos, long weight,
  116.                        long doc_id, time_t date,
  117.                        boolean capitalized, database* db,
  118.                        boolean word_position, boolean word_pairs));
  119.  
  120. static long
  121.   add_word_before_pairs(word, char_pos, line_pos,
  122.             weight, doc_id, date, capitalized, db,
  123.             word_position, word_pairs)
  124. char *word;    /* the word to be indexed, this could be a
  125.            word pair. If NULL there are no more words
  126.            to be indexed */
  127. long char_pos;    /* the position of the start of the
  128.            word */
  129. long line_pos;    /* this is passed for the best
  130.            section calculation */
  131. long weight;    /* how important the word looks
  132.            syntactically (such as is it bold)
  133.            NOT used by signature system */
  134. long doc_id;     /* current document, this will never be 0 */
  135. time_t date; /* display day of this document, 0 if not known */
  136. boolean capitalized; /* if the word started with a cap */
  137. database* db; /* database to insert the document */
  138. boolean word_position; /* if true, include word position in index. */
  139. boolean word_pairs; /* if true, add pairs of capitalized words */
  140. {
  141.   static char last_word[MAX_WORD_LENGTH + 1];
  142.   static long last_doc_id = -1;
  143.   /* The way it works is it remembers if the last word if it was
  144.      capitalized (if not it clears the saved word).  
  145.      If another capitalized word comes along next
  146.      (and it is in the same document), then it makes a joint word and calls 
  147.      add_word with it. 
  148.      
  149.      This does not throw away stopwords before forming pairs, so it will 
  150.      not be quite what CMDRS does.  This should only be used in seeker 
  151.      and serial searching before proximity is used.
  152.      
  153.      */
  154.   if(capitalized && word_pairs){
  155.     if(last_word[0] != '\0' && last_doc_id == doc_id){
  156.       add_word(make_joint_word(last_word, word), 
  157.            char_pos, line_pos, weight, doc_id, date, 1L, db);
  158.     }
  159.     else{
  160.       last_word[0] = '\0';
  161.     }
  162.     strncpy(last_word, word, MAX_WORD_LENGTH);
  163.     last_doc_id = doc_id;
  164.   }
  165.   else{                /* not capitalized or word_pairs is false */
  166.     last_word[0] = '\0';
  167.   }
  168.   return(add_word(word, char_pos, line_pos, weight, doc_id, date, 0L, db, word_position));
  169. }
  170.  
  171.  
  172. #ifdef NOTUSED
  173. #define WORD_LETTERS  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
  174.  
  175.  
  176. static char *new_word _AP((char* line,char* word));
  177.  
  178. static char *new_word(line,word)
  179. char *line;
  180. char *word;
  181. {
  182.   /* This copies the first word from line into word while downcasing it.
  183.      It returns a pointer into line that is after the word,
  184.      which can be used to call this function again.
  185.      If there are no words left, then NULL is returned,
  186.      and word is length 0.
  187.      There has got to be a better way.
  188.      */
  189.   long i = 0;
  190.   char *beginning_ptr = strpbrk(line, WORD_LETTERS);
  191.   char *next_word;
  192.   long length;
  193.   if(NULL == beginning_ptr){
  194.     word[0] = '\0';
  195.     return(NULL);
  196.   }
  197.   length  = strspn(beginning_ptr, WORD_LETTERS);
  198.   next_word = length + beginning_ptr;
  199.  
  200.   length = MIN(MAX_WORD_LENGTH,length);
  201.   for(i=0; i<length; i++){
  202.     word[i] = char_downcase((unsigned long)*beginning_ptr++);
  203.   }
  204.   word[i] = '\0';
  205.   return(next_word);
  206. }
  207.  
  208. static boolean reasonable_word _AP((char* word));
  209.  
  210. static boolean reasonable_word(word)
  211. char* word;
  212. /* this should be more sophisticated */
  213. {
  214.   if(strlen(word) > 1){
  215.     return(TRUE);
  216.   }
  217.   else{
  218.     return(FALSE);
  219.   }
  220. }
  221.  
  222. #endif /* def NOTUSED */
  223.  
  224.  
  225. /* MAPPING A FUNCTION OVER WORDS (QUICKLY) */
  226.  
  227.  
  228. /* map_over_words("foo bar baz", 0L, 1L, 0L, &integer, false, db, dummy_wordfunction) */
  229. static long dummy_wordfunction(word, char_pos, line_pos,
  230.                    weight, doc_id, date, capitalized, db)
  231.      char *word;    /* the word to be indexed, this could be a
  232.                word pair. If NULL there are no more words
  233.                to be indexed */
  234.      long char_pos;    /* the position of the start of the
  235.                word */
  236.      long line_pos;    /* this is passed for the best
  237.                section calculation */
  238.      long weight;    /* how important the word looks
  239.                syntactically (such as is it bold)
  240.                NOT used by signature system */
  241.      long doc_id;     /* current document, this will never be 0 */
  242.      time_t date; /* display day of this document, 0 if not known */
  243.      boolean capitalized; /* if the word started with a cap */
  244.      database* db; /* database to insert the document */
  245. {
  246.   if(word != NULL)
  247.     printf("word: %s, char_pos: %ld\n", word, char_pos);
  248.   return(0);
  249. }
  250.   
  251. /* returns the number of words added, or -1 if an error occurred */
  252. long map_over_words(line,
  253.             document_id,
  254.             weight,
  255.             file_position_before_line,
  256.             line_length,
  257.             newline_terminated,
  258.             db,
  259.             wordfunction,
  260.             word_position, word_pairs)
  261. char* line;
  262. long document_id;
  263. long weight;
  264. long file_position_before_line;
  265. long *line_length;
  266. boolean *newline_terminated;
  267. database* db;
  268. wordfunc *wordfunction;
  269. boolean word_position, word_pairs;
  270. {
  271.   /* Add words to the index if it should be done. 
  272.    * Returns the number of words added.
  273.    * Should it return the amount of weight added?
  274.    * The line length is side effected with the length of the line.
  275.    * Newline_terminated is set based on whether the last character
  276.    * in the string was a newline.  If it was not, then it fgets probably
  277.    * did not retrieve the whole line.
  278.    */
  279.  
  280.   long position_in_word = 0;
  281.   long word_count = 0;
  282.   char word[MAX_WORD_LENGTH + 1];
  283.   unsigned long ch;
  284.   long char_count = 0;
  285.   boolean capitalized = false; /* if the word starts with a cap */
  286.  
  287.   for(ch = (unsigned char)line[char_count++]; 
  288.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  289.     boolean alnum = isalnum(ch);
  290.     if(alnum){
  291.       /* put the character in the word if not too long */
  292.       if(position_in_word == 0)
  293.     capitalized = isupper((unsigned long)ch)?true:false;
  294.       if(position_in_word < MAX_WORD_LENGTH){
  295.     word[position_in_word++] = char_downcase((unsigned long)ch);
  296.       }
  297.     }
  298.     else{ /* not an in a word */
  299.       if(position_in_word != 0){
  300.     /* then we have collected a word */
  301.     if(position_in_word > 1){ /* is it reasonable ? */
  302.       word[position_in_word] = '\0';
  303.       if(0 !=
  304.          (*wordfunction)(word,
  305.                  file_position_before_line + char_count, 
  306.                  0L, /* line_pos */
  307.                  weight, 
  308.                  document_id, 
  309.                  (time_t)0L,
  310.                  capitalized,
  311.                  db,
  312.                  word_position,
  313.                  word_pairs))
  314.         return(-1); /* error */
  315.        word_count++;
  316.     }
  317.     position_in_word = 0;
  318.       }
  319.     }
  320.   }
  321.   /* finish last word */
  322.   if(position_in_word > 1){ /* is it reasonable ? */
  323.     word[position_in_word] = '\0';
  324.     if(0 != (*wordfunction)(word,
  325.                 file_position_before_line + char_count, 
  326.                 0L,    /* line_pos */
  327.                 weight, 
  328.                 document_id, 
  329.                 (time_t)0L,
  330.                 capitalized,
  331.                 db,
  332.                 word_position, word_pairs))
  333.       return(-1);
  334.     word_count++;
  335.   }
  336.  
  337.   /* for debugging
  338.   if(char_count - 1 != strlen(line)) {
  339.     waislog(WLOG_HIGH, WLOG_ERROR, 
  340.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  341.   }
  342.   */
  343.   if(newline_terminated != NULL){
  344.     if('\n' != line[char_count-2])
  345.       *newline_terminated = false;
  346.     else
  347.       *newline_terminated = true;
  348.   }
  349.   if(line_length != NULL)
  350.     *line_length = char_count - 1;
  351.   return(word_count);
  352. }
  353.  
  354.  
  355. static long add_words_if_appropriate 
  356.   _AP((char* line,long document_id,long weight,long file_position_before_line,
  357.        long* line_length,boolean* newline_terminated,database* db,
  358.        boolean word_position, boolean word_pairs));
  359.  
  360. static long 
  361. add_words_if_appropriate(line,
  362.              document_id,
  363.              weight,
  364.              file_position_before_line,
  365.              line_length,
  366.              newline_terminated,
  367.              db, 
  368.              word_position, word_pairs)
  369. char* line;
  370. long document_id;
  371. long weight;
  372. long file_position_before_line;
  373. long *line_length;
  374. boolean *newline_terminated;
  375. database* db;
  376. boolean word_position, word_pairs;
  377. {
  378.   /* Add words to the index if it should be done. 
  379.    * Returns the number of words added.
  380.    * Should it return the amount of weight added?
  381.    * The line length is side effected with the length of the line.
  382.    * Newline_terminated is set based on whether the last character
  383.    * in the string was a newline.  If it was not, then it fgets probably
  384.    * did not retrieve the whole line.
  385.    */
  386.  
  387.   long position_in_word = 0;
  388.   long word_count = 0;
  389.   char word[MAX_WORD_LENGTH + 1];
  390.   unsigned long ch;
  391.   long char_count = 0;
  392.   boolean capitalized = false; /* if the word starts with a cap */
  393.  
  394.   for(ch = (unsigned char)line[char_count++]; 
  395.       ch != '\0'; ch = (unsigned char)line[char_count++]){
  396.     boolean alnum = isalnum(ch);
  397.     if(alnum){
  398.       /* put the character in the word if not too long */
  399.       if(position_in_word == 0)
  400.     capitalized = isupper((unsigned long)ch)?true:false;
  401.       if(position_in_word < MAX_WORD_LENGTH){
  402.     word[position_in_word++] = char_downcase((unsigned long)ch);
  403.       }
  404.     }
  405.     else{ /* not an in a word */
  406.       if(position_in_word != 0){
  407.     /* then we have collected a word */
  408.     if(position_in_word > 1){ /* is it reasonable ? */
  409.       word[position_in_word] = '\0';
  410.       add_word_before_pairs(word,
  411.                 file_position_before_line + char_count, 
  412.                 0L, /* line_pos */
  413.                 weight, 
  414.                 document_id, 
  415.                 (time_t)0L,
  416.                 capitalized,
  417.                 db,
  418.                 word_position, word_pairs);
  419.        word_count++;
  420.     }
  421.     position_in_word = 0;
  422.       }
  423.     }
  424.   }
  425.   /* finish last word */
  426.   if(position_in_word > 1){ /* is it reasonable ? */
  427.     word[position_in_word] = '\0';
  428.     add_word(word,
  429.          file_position_before_line + char_count, 
  430.          0L,        /* line_pos */
  431.          weight, 
  432.          document_id, 
  433.          (time_t)0L,
  434.          0L,
  435.          db);
  436.     word_count++;
  437.   }
  438.  
  439.   /* for debugging
  440.   if(char_count - 1 != strlen(line)) {
  441.     waislog(WLOG_HIGH, WLOG_ERROR, 
  442.         "char_count: %ld, strlen: %ld", char_count, strlen(line));
  443.   }
  444.   */
  445.   if('\n' != line[char_count-2])
  446.     *newline_terminated = false;
  447.   else
  448.     *newline_terminated = true;
  449.  
  450.   *line_length = char_count - 1;
  451.   return(word_count);
  452. }
  453.  
  454. static int nodecompare _AP((unsigned long* i,unsigned long* j));
  455.  
  456. static int
  457. nodecompare(i,j)
  458. unsigned long *i, *j;
  459. {
  460.   if (i[0] < j[0])
  461.     return(-1);
  462.   else if (i[0] > j[0])
  463.     return(1);
  464.   else
  465.     return(0);
  466. }
  467.  
  468. #define nodeRange 256 /* 2048 sprint nodes on a full sized machine - should
  469.                  be passed in */
  470. #define iterations_to_reorder 50 /* 1 is best but slow */
  471.  
  472. static void finish_document
  473.   _AP((char* header,char* line,long document_id,
  474.        document_table_entry* the_document_table_entry,
  475.        long file_position_before_line,
  476.        long file_position_before_document,database* db,
  477.        boolean word_position, boolean word_pairs));
  478.  
  479. static void
  480. finish_document(header,line,document_id,the_document_table_entry,
  481.         file_position_before_line, file_position_before_document,
  482.         db, word_position, word_pairs)
  483. char* header;
  484. char* line;
  485. long document_id;
  486. document_table_entry* the_document_table_entry;
  487. long file_position_before_line;
  488. long file_position_before_document;
  489. database* db;
  490. boolean word_position, word_pairs;
  491. { long line_length;
  492.   boolean newline_terminated;
  493.   if(0 != strlen(header)){
  494.     /* add weights for the header (if there was one) */
  495.     long number_of_words =
  496.       map_over_words(header, document_id, 
  497.              extra_weight_for_header, 
  498.              file_position_before_line-
  499.              file_position_before_document,
  500.              &line_length, 
  501.              &newline_terminated,
  502.              db,
  503.              add_word_before_pairs,
  504.              word_position, word_pairs);
  505.     if(number_of_words == -1)
  506.       waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  507.     db->total_word_count += number_of_words;
  508.     the_document_table_entry->document_length += number_of_words;
  509.   }
  510.  
  511.   /* store out the document header here */
  512.   the_document_table_entry->headline_id = 
  513.     write_headline_table_entry(header, db);
  514.   if(NULL == line)
  515.     { /* EOF */
  516.       /* if it goes to the end of the file, then
  517.        * set the end_character to 0 so that it is clear that
  518.        * it goes to the end of the file.
  519.        */
  520.       the_document_table_entry->end_character = 0;
  521.     }
  522.   else                /* set the end_character */
  523.     the_document_table_entry->end_character = file_position_before_line;
  524.  
  525.  
  526.   /* 
  527.     waislog("start char: %ld, end char: %ld", 
  528.     the_document_table_entry->start_character,
  529.     the_document_table_entry->end_character);
  530.     */
  531.  
  532.   if (indexingForBeta)
  533.     { /* we need to decide which sprint node this doc will go in.
  534.      for now we will store the sn in the date field, but that
  535.      is temporary
  536.      NOTE that we must subract 1 from document_id, since we want
  537.      a 0 based number
  538.        */
  539.       static unsigned long* nodes = NULL; /* size/node# inited to 0 to 2047 */
  540.       static long minPos;
  541.       unsigned long size;
  542.      
  543.       if (nodes == NULL)
  544.        { long i;
  545.      long startPos;
  546.      time_t temp_time;
  547.  
  548.      nodes = (unsigned long*)s_malloc(sizeof(unsigned long)*nodeRange*2);
  549.      srand((int)time(&temp_time)); /* try to distribute the entries */
  550.      startPos = rand() % nodeRange; /* for indexes with < nodeRng docs */
  551.      for (i = 0; i < nodeRange; i++)
  552.       { nodes[(i * 2) + 1] = (i + startPos) % nodeRange; 
  553.         nodes[i * 2] = 0;
  554.       }
  555.      minPos = 0;
  556. /*printf("init: ");
  557. for (i = 0; i < nodeRange; i++)
  558.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  559. NL();*/
  560.        }
  561.  
  562.       /* place the document in the emptiest node (at minPos) */
  563.       the_document_table_entry->date = (time_t)nodes[(minPos * 2) + 1];
  564.  
  565.       /* increment the size to account for document */
  566.       size = nodes[minPos * 2];
  567.       size += (the_document_table_entry->end_character - 
  568.            the_document_table_entry->start_character);
  569.       nodes[minPos * 2] = size;
  570.  
  571. if ((the_document_table_entry->end_character - 
  572.      the_document_table_entry->start_character) > 100000)
  573.   printf("big doc %lu %s\n",the_document_table_entry->end_character - the_document_table_entry->start_character,header);
  574.  
  575.       minPos++;
  576.  
  577.       /* possibly reorder it */
  578.       if (minPos > iterations_to_reorder)
  579.        { 
  580. long i;
  581.      minPos = 0;
  582. /*printf("before: ");
  583. for (i = 0; i < nodeRange; i++)
  584.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  585. NL();*/
  586.      qsort((char*)nodes,nodeRange,sizeof(unsigned long) * 2,nodecompare);
  587. /*printf("after: ");
  588. for (i = 0; i < nodeRange; i++)
  589.  printf("<%lu,%lu> ",nodes[i*2],nodes[(i*2)+1]);
  590. NL();*/
  591. printf("just sorted nodes, min: ");
  592. for (i = 0; i < 10; i++)
  593.  printf("%lu ",nodes[i * 2]);
  594. printf(", max: %lu/%lu\n",nodes[(nodeRange * 2)-2],nodes[(nodeRange * 2)-1]); 
  595.        }
  596.  
  597.  
  598.  
  599. #ifdef old
  600.       sn = (document_id - 1) % 2048; /* 2048 = sn's in a full machine */
  601.  
  602.       /* should also take into account the "fullness" of any particular
  603.      node */
  604.       the_document_table_entry->date = (time_t)sn;
  605. /*      waislog(WLOG_LOW, WLOG_INFO, 
  606.           "put %s in sprint node %ld",header,sn);*/
  607. #endif /* def old */
  608.     }
  609.  
  610.   write_document_table_entry(the_document_table_entry, db);
  611.   cprintf(PRINT_AS_INDEXING, ".");
  612.   total_indexed_file_length =    /* set this so the speed looks right */
  613.     total_indexed_file_length + file_position_before_line;
  614.   total_indexed_file_length =    /* set it back */
  615.     total_indexed_file_length - file_position_before_line;
  616. }
  617.  
  618. #define LENGTH_OF_NEWLINE 1 /* this will be 2 on a PC, I think */  
  619.  
  620. void index_text_file(filename,
  621.              separator_function,
  622.              header_function,
  623.              date_function,
  624.              finish_header_function, 
  625.              type,
  626.              db,
  627.              check_for_text_file,
  628.              check_for_file_already_indexed,
  629.              word_position, word_pairs)
  630. char* filename;
  631. boolfunc *separator_function;
  632. voidfunc *header_function;
  633. longfunc *date_function;
  634. voidfunc *finish_header_function;
  635. char *type;
  636. database* db;
  637. boolean check_for_text_file;
  638. boolean check_for_file_already_indexed;
  639. boolean word_position, word_pairs;
  640. {
  641.   /* Addes words to the index for a given file.  
  642.    * The function arguments can be NULL which means it would 
  643.    *  always answer NULL.  
  644.    * separator_function is called on every line to see if it 
  645.    *  separates documents.  
  646.    * header_function is called on every line so that a headline 
  647.    *  can be accumulated.  This assumes that it will side effect global 
  648.    *  variables.
  649.    * finish_header_function is called when the document is finished 
  650.    *  (by separator function responding TRUE or EOF) this will return 
  651.    *  the headline string or NULL. 
  652.    *  Presumably finish_header_function will use the
  653.    *  effects of header_function.  finish_header_function 
  654.    *  will only be called once, so it should clear whatever state 
  655.    *  header_function has set.
  656.    * if check_for_text_file then it looks to see if first character
  657.    *  in the file is a printable character.
  658.    * if check_for_file_already_indexed then it looks through the filename 
  659.    *  file to see if the file has not been indexed.  If it has,
  660.    *  then it is checked to see if it is up-to-date. (it does not 
  661.    *  kill the old entry (maybe it should)).
  662.    */
  663.  
  664.   long filename_id;
  665.   document_table_entry the_document_table_entry;
  666.   long document_id = next_document_id(db);
  667.   FILE* input_stream = s_fopen(filename, "r");
  668.   long file_position_before_line = 0;
  669.   long file_position_before_document = 0;
  670.   long date;
  671.   char header[MAX_LINE_LENGTH];
  672.   char line[MAX_LINE_LENGTH];
  673.   char newtype[MAX_LINE_LENGTH];
  674.   long file_size = 0;
  675.  
  676.   if(NULL == input_stream){
  677.     waislog(WLOG_HIGH, WLOG_ERROR, 
  678.         "File %s does not exist", filename);
  679.     /* then the is not a valid file to be indexed */
  680.     return;
  681.   }
  682.   if(check_for_file_already_indexed){
  683.     time_t time;
  684.     char full_path[MAX_FILENAME_LEN];
  685.     truename(filename, full_path);
  686.     if(true == filename_in_database(full_path, type, &time, db)){
  687.       /* check that it is the same time as this file */
  688.       if(time == file_write_date(filename)){
  689.     waislog(WLOG_HIGH, WLOG_INDEX, 
  690.         "File %s already indexed", filename);
  691.     s_fclose(input_stream);
  692.     return;
  693.       }
  694.     }
  695.   }
  696.     
  697.   /* Make the current filename accessible via global variables.
  698.    * Increment current_filecount so routines can efficiently detect
  699.    * changes in the current file.
  700.    * -- Prentiss Riddle, Rice ONCS, riddle@rice.edu, 5/6/92
  701.    */
  702.  
  703.   if(current_filename == NULL) current_filename = s_malloc(MAX_FILENAME_LEN+1);
  704.  
  705.   strncpy(current_filename, filename, MAX_FILENAME_LEN);
  706.   current_filecount++;
  707.  
  708.   if(check_for_text_file){ 
  709.     /* if we need this to be a text file, check the first character
  710.        for a printable character */
  711.     long ch = fgetc(input_stream);
  712.     /* printf("First character is '%c'\n", ch); */
  713.     if(EOF == ch || (!isprint(ch) && !isspace(ch))){
  714.       s_fclose(input_stream);
  715.       return;
  716.     }
  717.     ungetc(ch, input_stream);
  718.   }
  719.  
  720.   header[0] = '\0';        /* set it to the empty string */
  721.  
  722.   /* filter type */
  723.   if (filter_contents) {
  724.     char cmdline[MAX_LINE_LENGTH];  
  725.     FILE *f;
  726.     struct stat sb;
  727.  
  728.     /* no error checking yet -- fix fix! */
  729.     /* get header */
  730.     sprintf(cmdline, "%s -h %s", filter_program, filename);
  731.     f = popen(cmdline, "r");
  732.     if (f == NULL) {
  733.     waislog(WLOG_HIGH, WLOG_ERROR, "couldn't run filter program");
  734.     s_fclose(input_stream);
  735.     return;
  736.     }
  737.     /* get headline */
  738.     fgets(header, MAX_LINE_LENGTH, f);
  739.     header[strlen(header)-1] = '\0';        /* get rid of \n */
  740.     /* get type */
  741.     fgets(newtype, MAX_LINE_LENGTH, f);
  742.     newtype[strlen(newtype)-1] = '\0';        /* get rid of \n */
  743. /*    type = newtype;*/
  744.     strcpy(type, newtype);
  745.     /* get integer: 1 = index contents, 0 = don't */
  746.     fgets(line, MAX_LINE_LENGTH, f);
  747.     index_contents = atoi(line);
  748.     pclose(f);
  749.  
  750.     /* run file through filter and use that as input stream */
  751.     sprintf(cmdline, "%s -f %s", filter_program, filename);
  752.     f = popen(cmdline, "r");
  753.  
  754.     /* get size of file */
  755.     if (stat(filename, &sb) >= 0)
  756.     file_size = sb.st_size;
  757.  
  758.     s_fclose(input_stream);
  759.     input_stream = f;
  760.  
  761.   }
  762.   
  763.   /* write out the filename */
  764.   filename_id = write_filename_table_entry(filename, type, db);
  765.   
  766.   /*  (if (not *drop_table*) (make_drop_table)) maybe put in later */
  767.   
  768.   header_flag_1 = NULL;
  769.   the_document_table_entry.filename_id = filename_id;
  770.   the_document_table_entry.start_character = 0;
  771.   the_document_table_entry.document_length = 0;
  772.   the_document_table_entry.number_of_lines = 0;
  773.   the_document_table_entry.date = 0;
  774.  
  775.   while(TRUE){
  776.     long line_length;
  777.     boolean newline_terminated;
  778.     char* read_line_result;
  779.     boolean eof;
  780.  
  781.     /* printf("ftell: %ld\n", ftell(input_stream)); */
  782.     /* read a line */
  783.     read_line_result  = fgets(line, MAX_LINE_LENGTH, input_stream);
  784.     beFriendly();
  785.      
  786.     /* eof = feof(input_stream); */ /* zero means not eof */
  787.     eof    = !read_line_result; 
  788.     
  789.     the_document_table_entry.number_of_lines++;
  790.  
  791.     if(eof ||
  792.        ((NULL != separator_function) &&
  793.     separator_function(line))){
  794.   
  795.       /* we are processing a separator, therefore we should
  796.        * finish off the last document, and start a new one
  797.        */
  798.       if(NULL != finish_header_function){
  799.     finish_header_function(header);
  800.       }
  801.       if(0 == strlen(header)){
  802.     char full_path[1000];
  803.     char directory[1000];
  804.     truename(filename, full_path);
  805.     sprintf(header, "%s   %s", pathname_name(full_path),
  806.         pathname_directory(full_path, directory));
  807.       }
  808.       the_document_table_entry.number_of_lines--; /* dont count separator */
  809.       /* finish off the last */
  810.       finish_document(header, line, document_id,
  811.               &the_document_table_entry,
  812.               file_size ? file_size : (
  813.                eof?    /* if EOF, use file length */
  814.                file_length(input_stream):file_position_before_line
  815.                ), 
  816.               file_position_before_document,
  817.               db, word_position, word_pairs);
  818.       /* initialize the next one */
  819.       the_document_table_entry.filename_id = filename_id;
  820.       the_document_table_entry.start_character = file_position_before_line;
  821.       the_document_table_entry.number_of_lines = 1; /* count separator */
  822.       the_document_table_entry.date = 0;
  823.       file_position_before_document = file_position_before_line;
  824.       header[0] = '\0';
  825.  
  826.       document_id = next_document_id(db);    
  827.  
  828.       if(!eof)
  829.     {            /* not EOF */
  830.       if(NULL != header_function){
  831.         header_function(line);
  832.       }
  833.       if (date_function != NULL && 
  834.           (date = date_function(line)) > 0)
  835.         the_document_table_entry.date = date;
  836.       line_length = strlen(line);
  837.       newline_terminated = true;
  838.     }
  839.       else{            /* EOF */
  840.     /* printf("closing the file\n"); */
  841.     if (filter_contents)
  842.         pclose(input_stream);
  843.     else
  844.         s_fclose(input_stream);
  845.     return;
  846.       }
  847.     }
  848.         
  849.     else{               
  850.       /* not a separator or EOF so process the line */
  851.       long number_of_words;
  852.       if(NULL != header_function) header_function(line);
  853.       if (date_function != NULL && 
  854.       the_document_table_entry.date == 0 &&
  855.            (date = date_function(line)) > 0) 
  856.     the_document_table_entry.date = date;
  857.  
  858.  
  859.       if(index_contents ) {
  860.         if( _indexable_section) {
  861.       number_of_words = map_over_words(line, document_id, 1L, 
  862.                      file_position_before_line -
  863.                      file_position_before_document,
  864.                      &line_length, 
  865.                      &newline_terminated,
  866.                      db,    
  867.                      add_word_before_pairs,
  868.                      word_position, word_pairs);
  869.       if(number_of_words == -1)
  870.         waislog(WLOG_HIGH, WLOG_ERROR, "map_over_words failed");
  871.       the_document_table_entry.document_length += number_of_words;
  872.       len_of_files_since_last_delete += number_of_words;
  873.       len_of_files_since_last_flush += number_of_words;
  874.       db->total_word_count += number_of_words;
  875.         }
  876.         else
  877.           newline_terminated = 0;
  878.       }
  879.     }
  880.     if(newline_terminated)
  881.       file_position_before_line += (line_length + 
  882.                     LENGTH_OF_NEWLINE /* in case of crlf */
  883.                     - 1 /* fgets gets one newline */
  884.                     );
  885.     else
  886.       file_position_before_line = ftell(input_stream);
  887.  
  888.     
  889.     /* for debugging
  890.     if(file_position_before_line != ftell(input_stream)) {
  891.       waislog(WLOG_LOW, WLOG_INFO, "ftell: %ld, computed ftell: %ld", 
  892.          ftell(input_stream),
  893.          file_position_before_line);
  894.          }
  895.          */    
  896.  
  897.   }
  898. }
  899.  
  900.  
  901.  
  902.  
  903. /* return TRUE if it is a directory, FALSE otherwise */
  904. boolean directoryp(file)
  905. char *file;
  906.  
  907. {
  908. #ifdef THINK_C
  909.   return(false);
  910. #else
  911.   struct stat stbuf;
  912.   if(stat(file, &stbuf) == -1)
  913.     return(FALSE);
  914.   if((stbuf.st_mode & S_IFMT) == S_IFDIR)
  915.     return(true);
  916.   return(FALSE);
  917. #endif
  918. }
  919.  
  920. /* return true if it is a file, FALSE otherwise */
  921. boolean filep(file)
  922. char *file;
  923. {
  924. #ifdef THINK_C
  925.  return(probe_file(file));
  926. #else
  927.   struct stat stbuf;
  928.   if(stat(file, &stbuf) == -1)
  929.     return(FALSE);
  930.   if(!((stbuf.st_mode & S_IFMT) == S_IFDIR))
  931.     return(true);
  932.   return(FALSE);
  933. #endif
  934. }
  935.  
  936. /* recursively indexes the directory specified. 
  937.  * If it is a file, then index it. 
  938.  */
  939. void index_directory(file,
  940.              separator_function,
  941.              header_function,
  942.              date_function,
  943.              finish_header_function, 
  944.              type,
  945.              db,
  946.              check_for_text_file,
  947.              check_for_file_already_indexed,
  948.              word_position, word_pairs)
  949. char *file;
  950. boolfunc *separator_function;
  951. voidfunc *header_function;
  952. longfunc *date_function;
  953. voidfunc *finish_header_function;
  954. char *type;
  955. database* db;
  956. boolean check_for_text_file;
  957. boolean check_for_file_already_indexed;
  958. boolean word_position, word_pairs;
  959. {
  960. #ifndef THINK_C
  961.   struct dirent **list;
  962.   long i, j;
  963.  
  964.   if(filep(file)){
  965.     waislog(WLOG_MEDIUM, WLOG_INDEX,
  966.         "Indexing file: %s",  file);
  967.     index_text_file(file, separator_function,
  968.             header_function, 
  969.             date_function,
  970.             finish_header_function,
  971.             type,
  972.             db,
  973.             check_for_text_file,
  974.             check_for_file_already_indexed,
  975.             word_position, word_pairs);
  976.   }
  977.   else if(directoryp(file)){
  978.     if ((i = scandir(file, &list, NULL, NULL)) < 0) {
  979.       return;
  980.     }
  981.     for(j = 0; j < i; j++) {
  982.       char name[1000];        /* max filename size */
  983.  
  984.       if(strcmp(list[j]->d_name, ".") == 0
  985.      || strcmp(list[j]->d_name, "..") == 0
  986.      )
  987.     continue;
  988.  
  989.       strcpy(name, file);    /* copy the filename into the name variable */
  990.       strcat(name, "/");
  991.       strcat(name, list[j]->d_name);
  992.       index_directory(name, separator_function,
  993.               header_function, 
  994.               date_function,
  995.               finish_header_function,
  996.               type,
  997.               db,
  998.               check_for_text_file,
  999.               check_for_file_already_indexed, 
  1000.               word_position, word_pairs);
  1001.     }
  1002.     if(list != NULL) {
  1003.       for (j = 0; j < i; j++)
  1004.     if(list[j] != NULL) free((char *)list[j]);
  1005.       free((char *)list);
  1006.     }
  1007. #endif                /*ndef THINK_C */
  1008.   }
  1009. }
  1010.  
  1011.  
  1012.  
  1013.